package com.gitee.wsl.mathematics.function.pso

import kotlin.math.abs
import kotlin.random.Random

/**
 * @param
 * @DATA
 * @Author LiDaPeng
 * @Description 粒子群
 */
class PSO(
    dimensionNub: Int,
    private val minBorder: FloatArray?,
    private val maxBorder: FloatArray?,
    //迭代次数
    private val times: Int,
    particleNub: Int,
    //粒子群执行函数
    private val psoFunction: PsoFunction,
    inertialFactor: Float,
    selfStudyFactor: Float,
    socialStudyFactor: Float,
    //取最大值还是最小值
    private val isMax: Boolean,
    private val maxSpeed: Float, //初始速度
    private val initSpeed: Float,
) {
    private var globalValue = -1f //当前全局最优值
    private var allPar: MutableList<Particle> = ArrayList<Particle>() //全部粒子集合
    private var inertialFactor = 0.5f //惯性因子
    private var selfStudyFactor = 2f //个体学习因子
    private var socialStudyFactor = 2f //社会学习因子
    val allBest: FloatArray = FloatArray(dimensionNub) //全局最佳位置
    private val random: Random = Random.Default

    /**
     * 初始化
     *
     * @param dimensionNub 维度
     * @param minBorder  最小边界
     * @param maxBorder 最大边界
     * @param times 迭代次数
     * @param particleNub 粒子数量
     * @param psoFunction 适应函数
     * @param inertialFactor 惯性因子
     * @param selfStudyFactor 个体学习因子
     * @param socialStudyFactor 社会学习因子
     * @param isMax 最大值是否为最优
     * @param maxSpeed 最大速度
     * @param initSpeed 初始速度
     * @throws Exception
     */
    init {
        if (inertialFactor > 0) {
            this.inertialFactor = inertialFactor
        }
        if (selfStudyFactor in 0.0..4.0) {
            this.selfStudyFactor = selfStudyFactor
        }
        if (socialStudyFactor in 0.0..4.0) {
            this.socialStudyFactor = socialStudyFactor
        }
        for (i in 0 until particleNub) { //初始化生成粒子群
            val particle: Particle = Particle(dimensionNub)
            allPar.add(particle)
        }
    }

    fun setAllPar(allPar: MutableList<Particle>) { //外置粒子群注入
        this.allPar = allPar
    }

    @Throws(Exception::class)
    fun start() { //开始进行迭代
        val size = allPar.size
        for (i in 0 until times) {
            for (j in 0 until size) {
                move(allPar[j], j)
            }
        }
        //粒子群移动结束
        // draw("/Users/lidapeng/Desktop/test/testOne/e2.jpg", fatherX, fatherY);
    }

    @Throws(Exception::class)
    private fun move(particle: Particle, id: Int) { //粒子群开始移动
        val parameter: FloatArray = particle.parameter//当前粒子的位置
        val bestData = particle.bestDataArray //该粒子的信息
        val value = psoFunction.getResult(parameter, id)
        val selfValue = particle.selfBestValue //局部最佳值
        if (isMax) { //取最大值
            if (value > globalValue) { //更新全局最大值
                globalValue = value
                //更新全局最佳位置
                for (i in allBest.indices) {
                    allBest[i] = parameter[i]
                }
            }
            if (value > selfValue) { //更新局部最大值
                particle.selfBestValue = value
                //更新局部最佳位置
                for (i in bestData.indices) {
                    bestData[i]!!.selfBestPosition = parameter[i]
                }
            }
        } else { //取最小值
            if (globalValue < 0 || value < globalValue) { //更新全局最小值
                globalValue = value
                //更新全局最佳位置
                for (i in allBest.indices) {
                    allBest[i] = parameter[i]
                }
            }
            if (selfValue < 0 || value < selfValue) { //更新全局最小值
                particle.selfBestValue = value
                //更新局部最佳位置
                for (i in bestData.indices) {
                    bestData[i]!!.selfBestPosition = parameter[i]
                }
            }
        }
        //先更新粒子每个维度的速度
        for (i in bestData.indices) {
            var speed = bestData[i]!!.speed //当前维度的速度
            val pid = bestData[i]!!.selfBestPosition //当前自己的最佳位置
            val selfPosition = parameter[i] //当前自己的位置
            val pgd = allBest[i] //当前维度的全局最佳位置
            //当前维度更新后的速度
            speed =
                (inertialFactor * speed + selfStudyFactor * random.nextFloat() * (pid - selfPosition) + socialStudyFactor * random.nextFloat() * (pgd - selfPosition))
            if (abs(speed) > maxSpeed) {
                speed = if (speed > 0) {
                    maxSpeed
                } else {
                    -maxSpeed
                }
            }
            bestData[i]!!.speed = speed
            //更新该粒子该维度新的位置
            var position = selfPosition + speed
            if (minBorder != null) {
                if (position < minBorder[i]) {
                    position = minBorder[i]
                }
                if (position > maxBorder!![i]) {
                    position = maxBorder[i]
                }
            }
            bestData[i]!!.selfBestPosition = position
        }
    }

    inner class Particle(dimensionNub: Int) {
        //粒子
        //初始化随机位置
        val bestDataArray: Array<BestData?> = arrayOfNulls(dimensionNub)
        var selfBestValue: Float = -1f //自身最优的值

        val parameter: FloatArray
            get() { //获取粒子位置信息
                val parameter = FloatArray(bestDataArray.size)
                for (i in parameter.indices) {
                    parameter[i] = bestDataArray[i]!!.selfBestPosition
                }
                return parameter
            }

        init {
            for (i in 0 until dimensionNub) {
                var position: Float
                if (minBorder != null && maxBorder != null) {
                    val min = minBorder[i]
                    val max = maxBorder[i]
                    val region = max - min + 1
                    position = random.nextInt(region.toInt()) + min //初始化该维度的位置
                } else {
                    position = random.nextFloat()
                }
                bestDataArray[i] = BestData(position, initSpeed)
            }
        }
    }

    inner class BestData(
        var selfBestPosition: Float,//当前维度自身最优的历史位置/自己最优位置的值
        var speed: Float //该粒子当前维度的速度
    ) {
        //private val selfPosition: Float //当前维度自己现在的位置/也就是当前维度自己的值

        //数据保存
        //init {
        //    this.selfBestPosition = selfBestPosition
        //    speed = initSpeed
        //}
    }
}
